#####################################################################
# File:         BooleanComponent.py
# Product:      KnowledgeEngine
#
# Copyright:    Copyright (c) 2001, Faulkner Ltd.
# Author:       Brian Oliver
#
# Description:  This file defines the base class for Boolean value
#               based concrete components
#
# See Also:     Component, Model, Section
#####################################################################

#####################################################################
## Knowledge Engine Library Imports
from Common import *
from Component import Component, ComponentInfo
from Assessible import Assessible
from KERLExtensions import KERLExtensions
from Report import *
from KERFProcessor import *
from PropertyDefinition import *
from PropertyTypes import *
from PropertyFormats import *
from PropertyStructures import *

#####################################################################
## Zope Library Imports
# (none)

#####################################################################
## Python Library Imports
import types

#####################################################################
## BooleanComponentInfo Class Definition
class BooleanComponentInfo(ComponentInfo):
    """
        Defines the class info methods, defining information about
        the class without needing an instance of the class
    """
    
    #---------------------------------------------------------------#
    
    def getClassName(self):
        """
            Returns the name of the class for which 
            we are providing information.
        """
        return 'BooleanComponent'

    #---------------------------------------------------------------#

    def getDisplayName(self):
        """
            Return a friendly name for the class instances
            (this is used for displaying in lists etc)
        """
    
        return "Boolean Component"
        
    #---------------------------------------------------------------#

    def getIdentityType(self):
        """
            ABSTRACT: Return the identity type.  
            
            This is a short string used for prefixing object identities
        """
        
        return "booleancomponent"

    #---------------------------------------------------------------#

    def isInstanceOf(self, className):
        """
            Returns if the class is an instance of the specified
            className, or implements the interface specified 
            by the className
        """

        return className == 'BooleanComponent' or className == 'Assessible' or ComponentInfo.isInstanceOf(self, className)
    
    #---------------------------------------------------------------#
        
    def removeState(self, model):
        """
            Remove all component state for the specified model
            for this component type
        """

        #the delete query
        sql = "DELETE FROM booleans WHERE modelid='%s'" % (model.getIdentity(),)

        #perform the query
        model.getDatabaseConnection().query(sql)
    
    #---------------------------------------------------------------#
        
    def deleteAssessment(self, model, identity):
        """
            Remove the assessment state for the specified model 
            and assessment identity
        """

        #the delete query
        sql = "DELETE FROM booleans WHERE modelid='%s' AND assessmentid='%d'" % (model.getIdentity(), identity)

        #perform the query
        model.getDatabaseConnection().query(sql)
    
    #---------------------------------------------------------------#

    def onConfigureProperties(self):
        """
            Automatically called by the Knowledge Engine once
            the class has been loaded.  Use this method to register 
            class specific properties using the PropertyReflection interface.
            
            If this class is sub-classed, a call must be made to
            the super-class to ensure super-class properties are
            correctly registered
            
            NOTE: This method is called before ClassInfo.onAfterStartup()            
        """

        #initialise the super-class properties for reflection
        ComponentInfo.onConfigureProperties(self)

        #add the 'reportContentWhenSelected' property        
        self.addPropertyDefinition(PropertyDefinition(identity="reportContentWhenSelected", displayName="Selected Report Content", description="Text to be displayed in a report for this component when it is selected/true/on/yes.", defaultValue="", propertyType=pt_STRING, propertyFormat=pf_HTML, propertyStructure=ps_ATOMIC))        

        #add the 'reportContentWhenUnselected' property        
        self.addPropertyDefinition(PropertyDefinition(identity="reportContentWhenUnselected", displayName="Unselected Report Content", description="Text to be displayed in a report for this component when it is unselected/false/off/no.", defaultValue="", propertyType=pt_STRING, propertyFormat=pf_HTML, propertyStructure=ps_ATOMIC))        

        #add the 'defaultValue' property        
        self.addPropertyDefinition(PropertyDefinition(identity="defaultValue", displayName="Default Value", description="The default value for this component in new assessments", defaultValue=false, propertyType=pt_BOOLEAN, propertyFormat=pf_SELECTION + pf_SELECTED_UNSELECTED, propertyStructure=ps_ATOMIC))        
        
    #---------------------------------------------------------------#

#####################################################################
## BooleanComponent Class Definition
class BooleanComponent(Component, Assessible, KERLExtensions):
    """
        Defines the base class for all boolean based assessment
        components.
    """
    
    #---------------------------------------------------------------#

    #ABSTRACT TRANSIENT: a ClassInfo implementation instance to provide info about this class
    _CLASSINFO = BooleanComponentInfo()

    #---------------------------------------------------------------#

    def __init__(self, id, identity):
        "Constructor for the BooleanComponent"        

        Component.__init__(self, id, identity)
        
        #perform onAfterInstanciation processing
        self.onAfterInstanciation()

    #---------------------------------------------------------------#

    def onAfterInstanciation(self):
        """
            Handle Knowledge Engine specific actions after this object 
            has been loaded into memory.  
            
            This is useful for performing backwards compatibility
        """
        
        #perform Component.onAfterInstanciation processing
        Component.onAfterInstanciation(self)
        
        ###print "DEBUG: BooleanComponent.onAfterInstanciation: Completed"

    #---------------------------------------------------------------#

    def onAfterIdentityChange(self, identityMappings):
        """
            Handle Knowledge Engine specific actions after the 
            identity of this object has changed.
            
            The identityMappings dictionary holds 
            mappings from oldIdentities -> newIdentities
            (including this object)
                        
        """

        #perform default identity changes
        Component.onAfterIdentityChange(self, identityMappings)
        
        #resolve report KERFs
        self.setReportContentWhenSelected(updateKERF(self.getReportContentWhenSelected(), identityMappings))
        self.setReportContentWhenUnselected(updateKERF(self.getReportContentWhenUnselected(), identityMappings))        
        
    #---------------------------------------------------------------#
        
    def getReportContentWhenSelected(self):
        "Returns the Report Content for the component when selected"        
        return self.reportContentWhenSelected

    #---------------------------------------------------------------#

    def setReportContentWhenSelected(self, reportContentWhenSelected):
        "Set the Report Content for the component when selected"        
        self.reportContentWhenSelected = reportContentWhenSelected
                
    #---------------------------------------------------------------#
        
    def getReportContentWhenUnselected(self):
        "Returns the Report Content the component when unselected"        
        return self.reportContentWhenUnselected

    #---------------------------------------------------------------#

    def setReportContentWhenUnselected(self, reportContentWhenUnselected):
        "Set the Report Content for the component when unselected"        
        self.reportContentWhenUnselected = reportContentWhenUnselected
                
    #---------------------------------------------------------------#
        
    def getDefaultValue(self):
        "Returns the component assessment default value (0 = false, 1 = true)"        
        return self.defaultValue == 'on'

    #---------------------------------------------------------------#

    def setDefaultValue(self, defaultValue):
        "Set the component assessment default value (0 = false, 1 = true)"        
        self.defaultValue = defaultValue
        
    #---------------------------------------------------------------#
        
    def hasDBState(self, model, assessmentIdentity, componentIdentity):
        """
            Return if the specified assessment identity is stored
            in the assessment database for this component.

            RETURN TYPE: Boolean
        """

        #create the sql query            
        sql = "SELECT value FROM booleans WHERE cid='%s' and assessmentid='%s'" % (componentIdentity, assessmentIdentity)

        #perform the query
        (colums, resultset) = model.getDatabaseConnection().query(sql)

        #return if there is a result
        return len(resultset) > 0
        
    #---------------------------------------------------------------#
        
    def getDBState(self, model, assessmentIdentity, componentIdentity):
        """
            Return the state of the assessment identity from the
            database.

            RETURN TYPE: Boolean
        """

        #create the sql query            
        sql = "SELECT value FROM booleans WHERE cid='%s' and assessmentid='%s'" % (componentIdentity, assessmentIdentity)

        #perform the query
        (colums, resultset) = model.getDatabaseConnection().query(sql)

        #return the result
        if len(resultset) > 0:
            result = resultset[0][0]
        else:
            result = self.getDefaultValue()
        
        return result == true or result == 'on' or result == 'checked' or result == 'selected' or result == 'true'
        
    #---------------------------------------------------------------#
        
    def getState(self, model, assessmentIdentity, componentIdentity):
        """
            Return the state of the specified assessment identity
            for this component.
            
            RETURN TYPE: Boolean
        """

        return self.getDBState(model, assessmentIdentity, componentIdentity)
        
    #---------------------------------------------------------------#
        
    def setDBState(self, model, assessmentIdentity, componentIdentity, value):
        """
            Set the state of the specified assessment identity
            to the specified value in the database for this component

            VALUE TYPE: Boolean
        """

        #is the component state already in the database?
        #(used to determine the type of query)
        if self.hasDBState(model, assessmentIdentity, componentIdentity):
            sql = "UPDATE booleans SET value=%d WHERE cid='%s' and assessmentid='%s'" % (value, componentIdentity, assessmentIdentity)
        else:
            sql = "INSERT INTO booleans (cid, basecid, assessmentid, value, modelid) VALUES ('%s', '%s', '%s', %d, '%s')" % (componentIdentity, self.getIdentity(), assessmentIdentity, value, model.getIdentity())

        #perform the query
        model.getDatabaseConnection().query(sql)
         
    #---------------------------------------------------------------#
        
    def setState(self, model, assessmentIdentity, componentIdentity, value):
        """
            Set the state of the specified assessment identity
            to the specified value for this component

            VALUE TYPE: Boolean OR String
        """
        
        #ensure the value is a boolean
        if type(value) is types.StringType:
            value = value == 'on' or value == 'checked' or value == 'selected' or value == 'true'

        self.setDBState(model, assessmentIdentity, componentIdentity, value)
    
    #---------------------------------------------------------------#
        
    def removeState(self, componentContext = None):
        """
            ABSTRACT: Remove the state of the component from all
            assessments given the provided context
        """

        #is the component in a library?
        if componentContext == None and self.isInLibrary():
            #the delete query for library components
            sql = "DELETE FROM booleans WHERE basecid='%s'" % (self.getIdentity(),)        
            
        else:
            #the delete query for non-library components
            sql = "DELETE FROM booleans WHERE cid='%s'" % (self.getIdentity(componentContext),)

        #perform the query
        self.getEnvironment().getDatabaseConnection().query(sql)
        
    #---------------------------------------------------------------#

    def getTypedAssessmentValue(self, formValue):
        """
            Returns the correctly typed python value for the
            specified HTTP form value
            
            Called By: AssesssmentCache.setState
        """
        
        #ensure the assessment value is a boolean
        return (type(formValue) is types.StringType and (formValue == 'on' or formValue == 'checked' or formValue == 'selected' or formValue == 'true')) or formValue == true
        
    #---------------------------------------------------------------#

    def toJavaScript(self, expression, componentContext = None):
        """
            Returns a Python string that represents a JavaScript
            expression, which may be used to evaluate the status
            of the component class specific KERL expression.

            NOTE: this method is primarily used to produce
            JavaScript expressions that form the basis of the
            client-side showif technology.
            
            RETURN TYPE: String         
        """

        #example: cond="['bool1','selected']" returns 'document.all("bool1").value=="true"'
        #example: cond="['bool1','unselected']" returns 'document.all("bool1").value=="false"'

        #get the identity
        identity = self.getIdentity(componentContext)

        #get the value
        value = expression[1]

        #determine the result
        result = "document.all(\"" + identity + "\").value=="
        
        if value == "selected":
            return result + "\"true\""
        else:
            return result + "\"false\""

    #---------------------------------------------------------------#

    def evaluate(self, expression, assessmentCache, componentContext = None):
        """
            Evalutes the component class specific KERL expression
            using the provided assessmentCache.

            NOTE: this method is primarily used to produce
            JavaScript expressions that form the basis of the
            client-side showif technology.

            RETURN TYPE: Boolean         
        """
        
        #get the identity
        identity = self.getIdentity(componentContext)

        #get the value
        value = expression[1]

        #get the stored state (and ensure it is a boolean)
        state = assessmentCache.getState(identity)
        state = state == "true" or state == true           

        #evaluate the expression
        if value == "selected":
            
            return state == true           
        else:

            return state != true
    
    #---------------------------------------------------------------#

    def updateKERLIdentities(self, expression, identityMappings):
        """
            Returns the expression with the appropriate old identities
            (as identified by the identityMappings) replaced with 
            new identities.
        """
        
        #get the component identity from the expression (first element)
        identity = expression[0]
        
        #is the component in the identityMappings?
        if identityMappings.has_key(identity):
            expression[0] = identityMappings[identity]
        
        return expression
        
    #---------------------------------------------------------------#

    def getComponentProperty(self, identity, propertyName, assessmentCache = None):
        """
            Returns the value of the specified property, given the
            identity of the component (which may be used to lookup states) and
            an assessment cache.

            RETURNS: None if the property doesn't exist
        """

        if propertyName == "value":
            if assessmentCache.getState(identity):
                return "Yes"
            else:
                return "No"
        else:
            return Component.getComponentProperty(self, identity, propertyName, assessmentCache)
        
    #---------------------------------------------------------------#

#####################################################################
## Class Initialisation
# (none)
